﻿Imports System.ComponentModel
Imports System.Globalization

Public Class MultiColumnComboBox
    Inherits ComboBox

    Private columnPadding As Integer = 5
    Private columnWidths(0) As Single   '项宽度
    Private columnNames(0) As String  '项名称
    Private valueMemberColumnIndex As Integer = 0    'valueMember属性列所在的索引

    Public Sub New()
        InitItems()
    End Sub

    Public Sub New(container As IContainer)
        container.Add(Me)
        InitItems()
    End Sub

    Private Sub InitItems()
        Me.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable
        Me.DropDownStyle = ComboBoxStyle.DropDownList
    End Sub

    ''' <summary>
    ''' 初始化数据源各列的名称
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub InitializeColumns()
        Dim propertyDescriptorCollection = DataManager.GetItemProperties
        ReDim columnWidths(propertyDescriptorCollection.Count - 1)
        ReDim columnNames(propertyDescriptorCollection.Count - 1)

        For i = 0 To propertyDescriptorCollection.Count - 1
            Dim name = propertyDescriptorCollection(i).Name
            columnNames(i) = name
        Next
    End Sub

    Protected Overrides Sub OnValueMemberChanged(e As EventArgs)
        MyBase.OnValueMemberChanged(e)
        InitializeValueMemberColumn()
    End Sub

    ''' <summary>
    ''' 返回valuemember在显示列中的索引位置
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub InitializeValueMemberColumn()
        Dim colIndex = 0
        For Each columnName In columnNames
            If String.Compare(columnName, ValueMember, True, CultureInfo.CurrentUICulture) = 0 Then
                valueMemberColumnIndex = colIndex
                Exit For
            End If
            colIndex += 1
        Next
    End Sub

    ''' <summary>
    ''' 显示下拉框的时候出发
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnDropDown(e As EventArgs)
        MyBase.OnDropDown(e)
        Me.DropDownWidth = CalculateTotalWidth
    End Sub

    Private Function CalculateTotalWidth() As Single
        columnPadding = 5
        Dim totalWidth = 0.0F
        For Each w In columnWidths
            totalWidth += w + columnPadding
        Next
        Return totalWidth + SystemInformation.VerticalScrollBarWidth
    End Function

    ''' <summary>
    ''' 获取各列的宽度和项的总宽度
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnMeasureItem(e As MeasureItemEventArgs)
        MyBase.OnMeasureItem(e)
        If DesignMode Then
            Return
        End If
        InitializeColumns()
        For i = 0 To columnNames.Length - 1
            Dim item = FilterItemOnProperty(Items(e.Index), columnNames(i)).ToString
            Dim sizeF = e.Graphics.MeasureString(item, Font)
            columnWidths(i) = System.Math.Max(columnWidths(i), sizeF.Width)
        Next
        Dim totalWidth = CalculateTotalWidth()
        e.ItemWidth = totalWidth
    End Sub

    ''' <summary>
    ''' 绘制下拉框的内容
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnDrawItem(e As DrawItemEventArgs)
        MyBase.OnDrawItem(e)

        If DesignMode Then
            Return
        End If
        Dim boundsRect = e.Bounds
        e.Graphics.FillRectangle(Brushes.Bisque, e.Bounds)
        If (e.State And DrawItemState.Focus) = 0 Then
            e.Graphics.FillRectangle(Brushes.White, e.Bounds)
        End If

        Dim lastRight = 0
        Using linePen As New Pen(SystemColors.GrayText)
            Using brus As New SolidBrush(ForeColor)
                If columnNames.Length = 0 Then
                    e.Graphics.DrawString(Convert.ToString(Items(e.Index)), Font, brus, boundsRect)
                Else
                    For i = 0 To columnNames.Length - 1
                        Dim item = FilterItemOnProperty(Items(e.Index), columnNames(i)).ToString
                        boundsRect.X = lastRight
                        boundsRect.Width = columnWidths(i) + columnPadding
                        lastRight = boundsRect.Right

                        If i = valueMemberColumnIndex Then
                            Using f As New Font(Font, FontStyle.Bold)
                                e.Graphics.DrawString(item, f, brus, boundsRect)
                            End Using
                        Else
                            e.Graphics.DrawString(item, Font, brus, boundsRect)
                        End If

                        If i < columnNames.Length - 1 Then
                            e.Graphics.DrawLine(linePen, boundsRect.Right, boundsRect.Top, boundsRect.Right, boundsRect.Bottom)
                        End If
                    Next
                End If
            End Using
        End Using
        e.DrawFocusRectangle()
    End Sub
End Class
